/*
 * Copyright (C) 2012  Daniel Léonard
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */

package ca.forklabs.soap.axis.client;

import java.net.URL;
import org.apache.axis.client.Stub;
import ca.gc.dfo.wds.BoundaryDate;
import ca.gc.dfo.wds.BoundaryDepth;
import ca.gc.dfo.wds.BoundarySpatial;
import ca.gc.dfo.wds.Metadata;
import ca.gc.dfo.wds.ResultSet;
import ca.gc.dfo.wds.Status;
import ca.gc.shc.ws.client.PredictionsServiceLocator;
import ca.gc.shc.ws.client.PredictionsSoapBindingStub;

import java.net.MalformedURLException;
import java.rmi.RemoteException;
import javax.xml.rpc.ServiceException;
import org.apache.axis.AxisFault;

/**
 * Class {@code PredictionsClient} eases the communication with the prediction
 * web service of
 * <a href="http://www.dfo-mpo.gc.ca/">Fisheries and Oceans Canada</a>.
 * <p>
 * The default URL of the web service is
 * <a href="https://ws-shc.qc.dfo-mpo.gc.ca/predictions">https://ws-shc.qc.dfo-mpo.gc.ca/predictions</a>.
 *
 * @author   <a href="mailto:forklabs at gmail.com?subject=ca.forklabs.soap.axis.client.PredictionsClient">Daniel Léonard</a>
 */
public class PredictionsClient extends WDSClient {

//---------------------------
// enums
//---------------------------

   /**
    * Enum {@code DataType} describes the two possible data type that can be
    * queried from the predictions service.
    */
   public enum DataType {
      /** Data type for water level predictions. */
      wl15,
      /** Data type for low-tide and high-tide predictions. */
      hilo,
      }


//---------------------------
// Class variables
//---------------------------

   /** The default URL for the Spine service. */
   public static final String DEFAULT_URL = "https://ws-shc.qc.dfo-mpo.gc.ca/predictions"; //$NON-NLS-1$


//---------------------------
// Instance variables
//---------------------------

   /** The auto-generated bindings. */
   private PredictionsSoapBindingStub bindings;


//---------------------------
// Constructors
//---------------------------

   /**
    * Constructor.
    * @param   username   the username.
    * @param   password   the password.
    * @exception    AxisFault   if something goes wrong with Axis.
    * @exception    MalformedURLException   if something goes wrong with the
    *                                       default URL.
    * @exception   ServiceException   if something goes wrong with Axis.
    * @see   #DEFAULT_URL
    */
   public PredictionsClient(String username, String password) throws AxisFault, MalformedURLException, ServiceException {
      this(DEFAULT_URL, username, password);
      }

   /**
    * Constructor.
    * @param   url   the web service URL.
    * @param   username   the username.
    * @param   password   the password.
    * @exception    AxisFault   if something goes wrong with Axis.
    * @exception    MalformedURLException   if something goes wrong with the.
    *                                       url.
    * @exception   ServiceException   if something goes wrong with Axis.
    */
   public PredictionsClient(String url, String username, String password) throws AxisFault, MalformedURLException, ServiceException {
      this(new URL(url), username, password);
      }

   /**
    * Constructor.
    * @param   url   the web service URL.
    * @param   username   the username.
    * @param   password   the password.
    * @exception   AxisFault   if something goes wrong with Axis.
    * @exception   ServiceException   if something goes wrong with Axis.
    */
   @SuppressWarnings("hiding")
   public PredictionsClient(URL url, String username, String password) throws AxisFault, ServiceException {
      PredictionsServiceLocator locator = new PredictionsServiceLocator();
      PredictionsSoapBindingStub bindings = (PredictionsSoapBindingStub) locator.getpredictions(url);
      bindings.setUsername(username);
      bindings.setPassword(password);
      this.setBindings(bindings);
      }


//---------------------------
// Accessors and mutators
//---------------------------

   /**
    * Changes the Axis bindings.
    * @param   bindings   the new bindings.
    */
   protected void setBindings(PredictionsSoapBindingStub bindings) {
      this.bindings = bindings;
      }

   /**
    * Gets the Axis bindings.
    * @return   the bindings.
    */
   protected PredictionsSoapBindingStub getBindings() {
      return this.bindings;
      }

   /**
    * {@inheritDocs}
    */
   @Override
   protected Stub getBindingsStub() {
      Stub stub = this.getBindings();
      return stub;
      }


//---------------------------
// Delegate methods
//---------------------------

   /**
    * Gets the time interval data is available for. Boundary dates are expected
    * to be from the previous year to the next year.
    * @return   the boundary dates.
    * @exception   RemoteException   if anything goes wrong.
    */
   @Override
   @SuppressWarnings("hiding")
   public BoundaryDate getBoundaryDate() throws RemoteException {
      PredictionsSoapBindingStub bindings = this.getBindings();
      BoundaryDate boundaries = bindings.getBoundaryDate();
      return boundaries;
      }

   /**
    * Gets the depth interval data is available for. Data is always for the
    * surface and the depth interval is always {@code [0.0, 0.0]}.
    * @return   the boundary depths.
    * @exception   RemoteException   if anything goes wrong.
    */
   @Override
   @SuppressWarnings("hiding")
   public BoundaryDepth getBoundaryDepth() throws RemoteException {
      PredictionsSoapBindingStub bindings = this.getBindings();
      BoundaryDepth boundaries = bindings.getBoundaryDepth();
      return boundaries;
      }

   /**
    * Gets the spatial interval data is available for. Data is available for
    * all of Canada, roughly between 41 N and 84 N and 52 W and 141 W.
    * @return   the boundary coordinates.
    * @exception   RemoteException   if anything goes wrong.
    */
   @Override
   @SuppressWarnings("hiding")
   public BoundarySpatial getBoundarySpatial() throws RemoteException {
      PredictionsSoapBindingStub bindings = this.getBindings();
      BoundarySpatial boundaries = bindings.getBoundarySpatial();
      return boundaries;
      }

   /**
    * Gets the list of data types and their description. The predictions
    * service has a two data type {@code wl15} for water levels at 15 minute
    * intervals and {@code hilo} for the high tides and low tides. One of these
    * values must be given as the first parameter to
    * {@link #search(String, double, double, double, double, double, double, String, String, int, int, boolean, String, SortOrder)}.
    * @return   the list of data types and their descriptions.
    * @exception   RemoteException   if anything goes wrong.
    */
   @Override
   @SuppressWarnings("hiding")
   public Metadata[] getDataInfo() throws RemoteException {
      PredictionsSoapBindingStub bindings = this.getBindings();
      Metadata[] info = bindings.getDataInfo();
      return info;
      }

   /**
    * Gets the description of the predictions service.
    * @return   the description.
    * @exception   RemoteException   if anything goes wrong.
    */
   @Override
   @SuppressWarnings("hiding")
   public String getInfo() throws RemoteException {
      PredictionsSoapBindingStub bindings = this.getBindings();
      String info = bindings.getInfo();
      return info;
      }

   /**
    * Gets meta data about the predictions service.
    * @return   the list of meta data and their value.
    * @exception   RemoteException   if anything goes wrong.
    */
   @Override
   @SuppressWarnings("hiding")
   public Metadata[] getMetadata() throws RemoteException {
      PredictionsSoapBindingStub bindings = this.getBindings();
      Metadata[] info = bindings.getMetadata();
      return info;
      }

   /**
    * Gets the description of the various meta data.
    * @return   the list of meta data and their description.
    * @exception   RemoteException   if anything goes wrong.
    */
   @Override
   @SuppressWarnings("hiding")
   public Metadata[] getMetadataInfo() throws RemoteException {
      PredictionsSoapBindingStub bindings = this.getBindings();
      Metadata[] info = bindings.getMetadataInfo();
      return info;
      }

   /**
    * Gets the name of the predictions service.
    * @return   the name.
    * @exception   RemoteException   if anything goes wrong.
    */
   @Override
   @SuppressWarnings("hiding")
   public String getName() throws RemoteException {
      PredictionsSoapBindingStub bindings = this.getBindings();
      String name = bindings.getName();
      return name;
      }

   /**
    * Gets the status of the predictions service.
    * @return   the status.
    * @throws   RemoteException   if anything goes wrong.
    */
   @Override
   @SuppressWarnings("hiding")
   public Status getStatus() throws RemoteException {
      PredictionsSoapBindingStub bindings = this.getBindings();
      Status status = bindings.getStatus();
      return status;
      }

   /**
    * Gets the version of the predictions service.
    * @return   the version.
    * @exception   RemoteException   if anything goes wrong.
    */
   @Override
   @SuppressWarnings("hiding")
   public String getVersion() throws RemoteException {
      PredictionsSoapBindingStub bindings = this.getBindings();
      String version = bindings.getVersion();
      return version;
      }

   /**
    * Searches high tides and low tides.
    * @param   min_lat   the minimum latitude of the search box.
    * @param   max_lat   the maximum latitude of the search box.
    * @param   min_long   the minimum longitude of the search box.
    * @param   max_long   the maximum longitude of the search box.
    * @param   min_date   the oldest value of the time box.
    * @param   max_date   the youngest value of the time box.
    * @param   start   the starting index in the list, usually but not always
    *                  {@code 1}.
    * @param   size   the number of data points to a maximum of the value
    *                 specified by meta data {@code "max_results"} (usually
    *                 1000).
    * @param   metadata   indicates if meta data should be added to each data
    *                     point.
    * @param   metadataSelection   either {@code ""} or one of the following:
    *                              {@code station_id=<5-digit-station-id} or
    *                              {@code station_name=<name-of-the-station>}.
    * @param   order   the sort order.
    * @return   the list of high tides and low tides matching the filters.
    * @exception   RemoteException   if anything goes wrong.
    */
   public ResultSet searchHILO(double min_lat, double max_lat,
                               double min_long, double max_long,
                               long min_date, long max_date,
                               int start, int size,
                               boolean metadata, String metadataSelection,
                               SortOrder order) throws RemoteException {
      String data = DataType.hilo.name();
      double min_depth = 0.0;
      double max_depth = 0.0;
      ResultSet results = this.search(data,
                                      min_lat, max_lat,
                                      min_long, max_long,
                                      min_depth, max_depth,
                                      min_date, max_date,
                                      start, size,
                                      metadata, metadataSelection,
                                      order);
      return results;
      }

   /**
    * Searches high tides and low tides.
    * @param   min_lat   the minimum latitude of the search box.
    * @param   max_lat   the maximum latitude of the search box.
    * @param   min_long   the minimum longitude of the search box.
    * @param   max_long   the maximum longitude of the search box.
    * @param   min_date   the oldest value of the time box.
    * @param   max_date   the youngest value of the time box.
    * @param   start   the starting index in the list, usually but not always
    *                  {@code 1}.
    * @param   size   the number of data points to a maximum of the value
    *                 specified by meta data {@code "max_results"} (usually
    *                 1000).
    * @param   metadata   indicates if meta data should be added to each data
    *                     point.
    * @param   metadataSelection   either {@code ""} or one of the following:
    *                              {@code station_id=<5-digit-station-id} or
    *                              {@code station_name=<name-of-the-station>}.
    * @param   order   the sort order.
    * @return   the list of high tides and low tides matching the filters.
    * @exception   RemoteException   if anything goes wrong.
    */
   public ResultSet searchHILO(double min_lat, double max_lat,
                               double min_long, double max_long,
                               String min_date, String max_date,
                               int start, int size,
                               boolean metadata, String metadataSelection,
                               SortOrder order) throws RemoteException {
      String data = DataType.hilo.name();
      double min_depth = 0.0;
      double max_depth = 0.0;
      ResultSet results = this.search(data,
                                      min_lat, max_lat,
                                      min_long, max_long,
                                      min_depth, max_depth,
                                      min_date, max_date,
                                      start, size,
                                      metadata, metadataSelection,
                                      order);
      return results;
      }

   /**
    * Searches water levels.
    * @param   min_lat   the minimum latitude of the search box.
    * @param   max_lat   the maximum latitude of the search box.
    * @param   min_long   the minimum longitude of the search box.
    * @param   max_long   the maximum longitude of the search box.
    * @param   min_date   the oldest value of the time box.
    * @param   max_date   the youngest value of the time box.
    * @param   start   the starting index in the list, usually but not always
    *                  {@code 1}.
    * @param   size   the number of data points to a maximum of the value
    *                 specified by meta data {@code "max_results"} (usually
    *                 1000).
    * @param   metadata   indicates if meta data should be added to each data
    *                     point.
    * @param   metadataSelection   either {@code ""} or one of the following:
    *                              {@code station_id=<5-digit-station-id} or
    *                              {@code station_name=<name-of-the-station>}.
    * @param   order   the sort order.
    * @return   the list of water levels matching the filters.
    * @exception   RemoteException   if anything goes wrong.
    */
   public ResultSet searchWL15(double min_lat, double max_lat,
                               double min_long, double max_long,
                               long min_date, long max_date,
                               int start, int size,
                               boolean metadata, String metadataSelection,
                               SortOrder order) throws RemoteException {
      String data = DataType.wl15.name();
      double min_depth = 0.0;
      double max_depth = 0.0;
      ResultSet results = this.search(data,
                                      min_lat, max_lat,
                                      min_long, max_long,
                                      min_depth, max_depth,
                                      min_date, max_date,
                                      start, size,
                                      metadata, metadataSelection,
                                      order);
      return results;
      }

   /**
    * Searches water levels.
    * @param   min_lat   the minimum latitude of the search box.
    * @param   max_lat   the maximum latitude of the search box.
    * @param   min_long   the minimum longitude of the search box.
    * @param   max_long   the maximum longitude of the search box.
    * @param   min_date   the oldest value of the time box.
    * @param   max_date   the youngest value of the time box.
    * @param   start   the starting index in the list, usually but not always
    *                  {@code 1}.
    * @param   size   the number of data points to a maximum of the value
    *                 specified by meta data {@code "max_results"} (usually
    *                 1000).
    * @param   metadata   indicates if meta data should be added to each data
    *                     point.
    * @param   metadataSelection   either {@code ""} or one of the following:
    *                              {@code station_id=<5-digit-station-id} or
    *                              {@code station_name=<name-of-the-station>}.
    * @param   order   the sort order.
    * @return   the list of high tides and low tides matching the filters.
    * @exception   RemoteException   if anything goes wrong.
    */
   public ResultSet searchWL15(double min_lat, double max_lat,
                               double min_long, double max_long,
                               String min_date, String max_date,
                               int start, int size,
                               boolean metadata, String metadataSelection,
                               SortOrder order) throws RemoteException {
      String data = DataType.wl15.name();
      double min_depth = 0.0;
      double max_depth = 0.0;
      ResultSet results = this.search(data,
                                      min_lat, max_lat,
                                      min_long, max_long,
                                      min_depth, max_depth,
                                      min_date, max_date,
                                      start, size,
                                      metadata, metadataSelection,
                                      order);
      return results;
      }

   /**
    * Searches either high and low tides or water levels.
    * @param   data   the data type, either {@code wl15} or {@code hilo}.
    * @param   min_lat   the minimum latitude of the search box.
    * @param   max_lat   the maximum latitude of the search box.
    * @param   min_long   the minimum longitude of the search box.
    * @param   max_long   the maximum longitude of the search box.
    * @param   min_depth   the depth, always {@code 0.0}.
    * @param   max_depth   the depth, always {@code 0.0}.
    * @param   min_date   the oldest value of the time box.
    * @param   max_date   the youngest value of the time box.
    * @param   start   the starting index in the list, usually but not always
    *                  {@code 1}.
    * @param   size   the number of data points to a maximum of the value
    *                 specified by meta data {@code "max_results"} (usually
    *                 1000).
    * @param   metadata   indicates if meta data should be added to each data
    *                     point.
    * @param   metadataSelection   either {@code ""} or one of the following:
    *                              {@code station_id=<5-digit-station-id} or
    *                              {@code station_name=<name-of-the-station>}.
    * @param   order   the sort order.
    * @return   the list of data matching the filters.
    * @exception   RemoteException   if anything goes wrong.
    * @deprecated   Use {@code searchHILO} or {@code searchWL15} instead.
    */
   @Override
   @Deprecated
   @SuppressWarnings("hiding")
   public ResultSet search(String data,
                           double min_lat, double max_lat,
                           double min_long, double max_long,
                           double min_depth, double max_depth,
                           String min_date, String max_date,
                           int start, int size,
                           boolean metadata, String metadataSelection,
                           SortOrder order) throws RemoteException {
      PredictionsSoapBindingStub bindings = this.getBindings();
      String sort_order = order.name();
      ResultSet results = bindings.search(data,
                                          min_lat, max_lat,
                                          min_long, max_long,
                                          min_depth, max_depth,
                                          min_date, max_date,
                                          start, size,
                                          metadata, metadataSelection,
                                          sort_order);
      return results;
      }

   }
